package com.dlc.shop.manager.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.dlc.shop.bean.vo.SkuStockVO;
import com.dlc.shop.bean.vo.StockPointSkuVO;
import com.dlc.shop.common.bo.MultiDecrementBO;
import com.dlc.shop.common.config.Constant;
import com.dlc.shop.common.config.aof.AofRedisConfig;
import com.dlc.shop.common.constants.CacheNames;
import com.dlc.shop.common.constants.LuaOperateEnum;
import com.dlc.shop.common.enums.MultiStockEnum;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.i18n.I18nMessage;
import com.dlc.shop.common.response.ResponseEnum;
import com.dlc.shop.common.util.RedisLuaUtil;
import com.dlc.shop.util.StockUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.data.redis.core.ConvertingCursor;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 库存中心向外提供的业务功能的 真正实现
 *
 * @author lanhai
 */
@Component
@AutoConfiguration
public class StockManager {

    private static final Logger LOG = LoggerFactory.getLogger(StockManager.class);
    /**
     * 这里的redis用到AofRedis这个类的配置
     *
     * @see AofRedisConfig
     */
    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 默认的仓库id
     */
    public static final Long WAREHOUSE_ID = 0L;

    /**
     * 库存锁定转销量Lua
     */
    public static final byte[] ORDER_MAKER_USE_STOCK_BYTES = RedisLuaUtil.initByte("lua/product/order/OrderMakerUseStock.lua");

    /**
     * 库存的扣减Lua
     */
    public static final byte[] ORDER_STOCK_BYTES = RedisLuaUtil.initByte("lua/product/order/OrderStock.lua");

    /**
     * 库存锁定还原lua
     */
    public static final byte[] ORDER_UNLOCK_STOCK_BYTES = RedisLuaUtil.initByte("lua/product/order/OrderUnlockStock.lua");

    /**
     * 库存锁定还原lua
     */
    public static final byte[] REFUND_INCREASE_STOCK_BYTES = RedisLuaUtil.initByte("lua/product/refund/RefundIncreaseStock.lua");

    /**
     * 订单退款库存处理lua
     */
    public static final byte[] REFUND_STOCK_HANDLE_BYTES = RedisLuaUtil.initByte("lua/product/refund/RefundStockHandle.lua");

    /**
     * 调拨订单锁定sku库存
     */
    public static final byte[] ALLOT_LOCK_STOCK_BYTES = RedisLuaUtil.initByte("lua/product/allot/AllotLockStock.lua");

    /**
     * 调拨订单减少锁定sku库存
     */
    public static final byte[] ALLOT_REDUCE_LOCK_STOCK_BYTES = RedisLuaUtil.initByte("lua/product/allot/AllotReduceLockStock.lua");

    /**
     * sku库存操作
     */
    public static final byte[] SKU_STOCK_BYTES = RedisLuaUtil.initByte("lua/product/SkuStock.lua");

    public static final String STOCK = "stock:";

    /**
     * 库存不足
     */
    public static final String NOT_ENOUGH = "-1";
    /**
     * 超过购买上限
     */
    public static final String MAX_RESULT = "-2";

    /**
     * 秒杀锁定库存
     */
    public static final byte[] SECKILL_ORDER_STOCK_BYTES = RedisLuaUtil.initByte("lua/seckill/order/DecrementStock.lua");
    /**
     * 秒杀订单取消，回退库存
     */
    public static final byte[] SECKILL_ORDER_CANCEL_STOCK_BYTES = RedisLuaUtil.initByte("lua/seckill/order/ReturnStock.lua");
    /**
     * 秒杀订单支付成功，删除锁定信息，删除订单
     */
    public static final byte[] SECKILL_ORDER_PAY_SUCCESS_STOCK_BYTES = RedisLuaUtil.initByte("lua/seckill/order/PaySuccessToStock.lua");
    /**
     * 秒杀订单退款，还原库存
     */
    public static final byte[] SECKILL_ORDER_REFUND_RECOVERY_STOCK_BYTES = RedisLuaUtil.initByte("lua/seckill/refund/OrderReturnStock.lua");
    /**
     * 秒杀订单退款，实际还原库存操作
     */
    public static final byte[] SECKILL_ORDER_REFUND_STOCK_HANDLE_BYTES = RedisLuaUtil.initByte("lua/seckill/refund/SeckillRefundStockHandle.lua");



    public StockManager(AofRedisConfig aofRedisConfig) {
        stringRedisTemplate = aofRedisConfig.getStringRedisTemplate();
    }


    /**
     * 销售订单下单的时候，锁定相应的sku的库存
     * <p>
     * 正常返回 订单id_仓库id_skuId_剩余库存,订单id_仓库id_skuId_剩余库存,...
     * 返回值为空，代表已经执行一次了，不需要重复执行
     *
     * @return 返回解锁成功的库存信息  数据格式： orderId + skuId
     */
    public String markerUseStock(List<SkuStockVO> skuStockList) {
        // 正常返回 订单id_仓库id_skuId_剩余库存,订单id_仓库id_skuId_剩余库存...
        // 返回值为空，代表已经执行一次了，不需要重复执行
        return executeBySkuStockLocks(skuStockList, ORDER_MAKER_USE_STOCK_BYTES);
    }


    /**
     * 销售订单下单的时候，锁定相应的sku的库存
     * <p>
     * 正常返回 订单id_仓库id_skuId_剩余库存,订单id_仓库id_skuId_剩余库存,...
     * 异常返回 skuId
     */
    public String lockStock(List<SkuStockVO> skuStockList) {
        // 正常返回 订单id_仓库id_skuId_剩余库存,订单id_仓库id_skuId_剩余库存,...
        // 异常返回 skuId
        return executeBySkuStockLocks(skuStockList, ORDER_STOCK_BYTES);
    }


    public Map<String, SkuStockVO> unLockStock(List<SkuStockVO> skuStockLocks) {
        // 返回 订单id_仓库id_skuId_剩余库存,订单id_仓库id_skuId_剩余库存,...
        List<String[]> list = new ArrayList<>();
        for (SkuStockVO skuStockVO : skuStockLocks) {
            if (Objects.isNull(skuStockVO.getStockPointId())) {
                skuStockVO.setStockPointId(Constant.ZERO_LONG);
            }
            String[] strArray = StockUtil.loadQueryArray(
                    skuStockVO.getOrderNumber(),
                    skuStockVO.getStockPointId(),
                    skuStockVO.getSkuId(),
                    0,
                    skuStockVO.getDefaultPointId()
                    );
            list.add(strArray);
        }
        String result = RedisLuaUtil.execute(stringRedisTemplate, ORDER_UNLOCK_STOCK_BYTES, list);
        if (StrUtil.isBlank(result)) {
            return Collections.emptyMap();
        }
        List<SkuStockVO> skuStockList = StockUtil.listOrderResult(result);
        return skuStockList.stream().collect(
                Collectors.toMap(skuStockVO -> skuStockVO.getOrderNumber() + Constant.UNDERLINE + skuStockVO.getSkuId(),
                        Function.identity(),
                        (v1, v2) -> v1));
    }

    /**
     * 改变sku库存信息
     *
     * @param skuStocks      sku库存列表
     * @param luaOperateEnum 操作枚举 - 不是必传，如果skuStock.getOperateType()中有值就不用传此参数
     * @return
     */
    public String skuStock(List<SkuStockVO> skuStocks, LuaOperateEnum luaOperateEnum) {
        if (CollUtil.isEmpty(skuStocks)) {
            LOG.info("skuStock skuStocks is empty");
            return null;
        }

        List<String[]> dataList = new ArrayList<>(skuStocks.size());
        for (SkuStockVO skuStock : skuStocks) {
            // 插入操作类型
            if (Objects.nonNull(luaOperateEnum)) {
                skuStock.setOperateType(luaOperateEnum.value());
            }

            if (Objects.isNull(skuStock.getSkuId()) || Objects.isNull(skuStock.getOperateType())) {
                // 代码问题，skuId和操作类型必传，不能为null
                LOG.info("sku库存信息异常, SkuStock:{}", skuStock);
                throw new YamiShopBindException("sku库存信息异常");
            }
            // skuId_仓库id_库存变动数量_库存状态_库存操作
            dataList.add(StockUtil.loadQueryArray(skuStock.getSkuId(), skuStock.getStockPointId(),
                    skuStock.getStock(), skuStock.getStatus(), skuStock.getOperateType()));
        }

        // 传参 skuId_仓库id_库存变动数量_库存状态_库存操作, skuId_仓库id_库存变动数量_库存状态_库存操作
        // 返回 skuId_剩余库存_当前销量,skuId_剩余库存_当前销量...
        return executeLuk(dataList, SKU_STOCK_BYTES);

    }


    public String executeLuk(List<String[]> dataList, byte[] script) {
        if (CollUtil.isEmpty(dataList)) {
            LOG.info("executeLuk dataList is empty");
            return null;
        }
        try {
            return RedisLuaUtil.execute(stringRedisTemplate, script, dataList);
        } catch (Exception e) {
            LOG.error("executeLuk error， data: {}, message:{}", dataList, e.getMessage());
            throw new YamiShopBindException(ResponseEnum.EXCEPTION);
        }
    }


    /**
     * 正常返回一个连接的字符串如下：
     * 订单id_仓库id_skuId_剩余库存,订单id_仓库id_skuId_剩余库存,...
     * 异常根据脚本内容进行返回
     */
    public String executeBySkuStockLocks(List<SkuStockVO> skuStockList, byte[] script) {
        // 订单id_仓库id_skuId_lockNum,  订单id_仓库id_skuId_lockNum...
        long timeMillis = 0L;
        if (script == ORDER_STOCK_BYTES) {
            // 延迟五分钟解锁，避免=提交订单未执行完成
            timeMillis = DateUtil.offsetMinute(new Date(), Constant.UN_LOCK_STOCK_TIME).getTime();
        }
        List<String[]> list = new ArrayList<>();
        for (SkuStockVO skuStockVO : skuStockList) {
            if (Objects.isNull(skuStockVO.getStockPointId())) {
                skuStockVO.setStockPointId(Constant.ZERO_LONG);
            }
            String[] strArray = StockUtil.loadQueryArray(
                    skuStockVO.getOrderNumber(),
                    skuStockVO.getStockPointId(),
                    skuStockVO.getSkuId(),
                    skuStockVO.getStock(),
                    timeMillis);
            list.add(strArray);
        }
        return RedisLuaUtil.execute(stringRedisTemplate, script, list);
    }


    /*
      ===========================sku===================================
     */

    /**
     * 转换sku库存信息为对象
     *
     * @param stockPointIdList skuId列表
     */
    public Map<String, SkuStockVO> skuStockMap(List<StockPointSkuVO> stockPointIdList) {
        return listCache(stockPointIdList);
    }

    /**
     * 获取sku总库存
     *
     * @param skuIds
     * @return
     */
    public Map<Long, SkuStockVO> mapSkuTotalStock(List<Long> skuIds) {
        List<String> keys = new ArrayList<>(skuIds.size());
        for (Long skuId : skuIds) {
            keys.add(CacheNames.SKU_SOCK + skuId);
        }
        Map<Long, SkuStockVO> stockMap = new HashMap<>(skuIds.size());
        List<String> results = this.mGet(keys);
        for (int i = 0; i < skuIds.size(); i++) {
            Long skuId = skuIds.get(i);
            SkuStockVO skuStockVO = StockUtil.getSkuStockVO(skuId, results.get(i));
            stockMap.put(skuId, skuStockVO);
        }
        return stockMap;
    }

    public Map<Long, Integer> mapSkuStockBySkuIdAndStockPointId(List<Long> skuIds, Long stockPointId){
        if (CollUtil.isEmpty(skuIds)) {
            return new HashMap<>(0);
        }
        List<String> keys = new ArrayList<>(skuIds.size());
        List<String> cacheNames = new ArrayList<>(keys.size());
        for (Long skuId : skuIds) {
            String key =  StockUtil.skuPointKey(skuId, stockPointId);
            keys.add(key);
            cacheNames.add(CacheNames.SKU_SOCK + key);
        }
        List<String> values = mGet(cacheNames);
        Map<Long, Integer> stockVOMap = new HashMap<>(keys.size());

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            stockVOMap.put(Long.parseLong(key.split(Constant.UNDERLINE)[0]), StockUtil.getSkuStockVO(key, values.get(i)).getStock());
        }
        return stockVOMap;
    }


    /**
     * 获取sku总库存
     *
     * @param skuStockList
     * @return
     */
    public Map<String, SkuStockVO> mapSkuStockBySkuStocks(List<SkuStockVO> skuStockList) {
        List<String> keys = new ArrayList<>(skuStockList.size());
        List<String> cacheNames = new ArrayList<>(skuStockList.size());
        for (SkuStockVO stockPointSkuVO : skuStockList) {
            String key = StockUtil.skuPointKey(stockPointSkuVO.getSkuId(), stockPointSkuVO.getStockPointId());
            if (keys.contains(key)) {
                continue;
            }
            keys.add(key);
            cacheNames.add(CacheNames.SKU_SOCK + key);
        }
        List<String> values = mGet(cacheNames);
        Map<String, SkuStockVO> stockVOMap = new HashMap<>(keys.size());

        for (int i = 0; i <keys.size(); i++) {
            String key = keys.get(i);
            stockVOMap.put(key, StockUtil.getSkuStockVO(key, values.get(i)));
        }
        return stockVOMap;
    }

    /**
     * 转换sku库存信息为对象
     *
     * @param stockPointIdList skuId列表
     */
    public List<SkuStockVO> convertSkuStock(List<StockPointSkuVO> stockPointIdList) {
        Map<String, SkuStockVO> map = listCache(stockPointIdList);
        if (MapUtil.isEmpty(map)) {
            return Collections.emptyList();
        }
        return map.values().stream().toList();
    }

    /*
     * ============================ sku库存记录 ===========================
     */

    public Map<String, SkuStockVO> listCache(List<StockPointSkuVO> stockPointList) {
        if (CollUtil.isEmpty(stockPointList)) {
            return new HashMap<>(0);
        }
        List<String> keys = new ArrayList<>(stockPointList.size());
        List<String> cacheNames = new ArrayList<>(stockPointList.size());
        for (StockPointSkuVO stockPointSkuVO : stockPointList) {
            String key =  StockUtil.skuPointKey(stockPointSkuVO.getSkuId(), stockPointSkuVO.getStockPointId());
            keys.add(key);
            cacheNames.add(CacheNames.SKU_SOCK + key);
        }
        List<String> values = mGet(cacheNames);
        Map<String, SkuStockVO> stockVOMap = new HashMap<>(keys.size());

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            stockVOMap.put(key, StockUtil.getSkuStockVO(key, values.get(i)));
        }
        return stockVOMap;
    }

    // ================ mset  ================


    public List<String> mGet(Collection<String> keys) {
        for (String key : keys) {
            if (key.contains(StrUtil.SPACE)) {
                throw new YamiShopBindException(ResponseEnum.EXCEPTION);
            }
        }
        return stringRedisTemplate.opsForValue().multiGet(keys);
    }


    /**
     * list缓存放入并设置时间
     *
     * @param cacheMap 缓存的数据集合
     */
    public void mSet(Map<String, String> cacheMap) {
        if (MapUtil.isEmpty(cacheMap)) {
            return;
        }
        for (String key : cacheMap.keySet()) {
            if (key.contains(StrUtil.SPACE)) {
                throw new YamiShopBindException(ResponseEnum.EXCEPTION);
            }
        }
        // 没有设置失效时间
        stringRedisTemplate.opsForValue().multiSet(cacheMap);
    }


    /**
     * list缓存放入并设置时间
     *
     * @param keys 缓存的数据集合
     */
    public void mDel(List<String> keys) {
        if (CollUtil.isEmpty(keys)) {
            return;
        }
        stringRedisTemplate.delete(keys);
    }

    /**
     * 根据库存点id获取对应商品库存
     * @param stockPointIds 库存点id
     * @return 库存映射map
     */
    public Map<String, SkuStockVO> mapSkuStockByStockPointIds(List<Long> stockPointIds) {
        if (CollectionUtils.isEmpty(stockPointIds)) {
            return new HashMap<>(0);
        }
        Map<String, SkuStockVO> stockMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        Set<String> keys;
        SkuStockVO skuStockVO;
        for (Long stockPointId : stockPointIds) {
            // 找出匹配库存点的keys以及对应value
            keys = this.scanWithPatten(CacheNames.SKU_SOCK + "*" + Constant.UNDERLINE + stockPointId);
            if (CollectionUtils.isEmpty(keys)) {
                continue;
            }
            List<String> values = this.mGet(keys);
            // 将value转成skuStockVO，并存入Map
            int i=0;
            for (String key : keys) {
                key = key.substring(CacheNames.SKU_SOCK.length());
                skuStockVO = StockUtil.getSkuStockVO(key, values.get(i));
                stockMap.put(key, skuStockVO);
                i++;
            }
        }
        return stockMap;
    }
    public Map<String, SkuStockVO> scanSkuStock(String key) {
        Map<String, String> scan = scan(key);
        Map<String, SkuStockVO> stockMap = new HashMap<>(scan.size());
        for (Map.Entry<String, String> entry : scan.entrySet()) {
            String skuKey = entry.getKey();
            skuKey = skuKey.replace(CacheNames.SKU_SOCK, "");
            SkuStockVO skuStockVO = StockUtil.getSkuStockVO(skuKey, entry.getValue());
            stockMap.put(skuKey, skuStockVO);
        }
        return stockMap;
    }



    public Map<String, String> scan(String key) {
        Map<String, String> stockMap = Collections.emptyMap();
        Cursor<String> cursor = null;
        try {
            ScanOptions options = ScanOptions.scanOptions().count(1000).match(key + "*").build();
            RedisSerializer<String> redisSerializer = (RedisSerializer<String>) stringRedisTemplate.getKeySerializer();
            cursor = stringRedisTemplate.executeWithStickyConnection(redisConnection ->
                    new ConvertingCursor<>(redisConnection.keyCommands().scan(options), redisSerializer::deserialize));
            if(Objects.isNull(cursor)){
                return stockMap;
            }
            List<String> keys = new ArrayList<>(Constant.INITIAL_CAPACITY);
            while (cursor.hasNext()) {
                keys.add(cursor.next());
            }
            if (keys.isEmpty()) {
                return stockMap;
            }
            stockMap = new HashMap<>(keys.size());
            List<String> values = stringRedisTemplate.opsForValue().multiGet(keys);
            for (int j = 0; j < keys.size(); j++) {
                String stock = values.get(j);
                if (StrUtil.isBlank(stock)) {
                    continue;
                }
                stockMap.put(keys.get(j), stock);
            }
        } finally {
            if(Objects.nonNull(cursor)) {
                cursor.close();
            }
        }
        return stockMap;
    }

    /**
     * 获取符合匹配条件的关键词（非阻塞）
     * @param patten 匹配条件
     * @return 关键词集合
     */
    public Set<String> scanWithPatten(String patten) {
        Set<String> keys = new HashSet<>(16);
        ScanOptions options = ScanOptions.scanOptions()
                .match(patten)
                .count(1000)
                .build();
        try (Cursor<String> cursor = stringRedisTemplate.scan(options)) {
            while (cursor.hasNext()) {
                keys.add(cursor.next());
            }
        }
        return keys;
    }

    /**
     * 保存秒杀库存
     */
    public void doZone(MultiDecrementBO multiDecrementBO) {
        List<String> stockPointList = multiDecrementBO.getStockPointList();
        // Map<对应redis编号, Map<仓库id, 库存数量>>，  Map<仓库id, 库存数量>就是每个sku区域库存分片后的库存map
        Map<Long, Integer> map = new HashMap<>();
        for (String stockPointSkuVO: stockPointList) {
            String[] split = stockPointSkuVO.split(String.valueOf(StrUtil.C_UNDERLINE));
            // 库存点id
            Long stockPointId = Long.parseLong(split[0]);
            // 库存点库存
            Integer totalStock = Integer.parseInt(split[1]);
            map.put(stockPointId, totalStock);
        }
        // 计算总库存
        // 计算当前redis中的sku总库存数量
        AtomicInteger skuStock = new AtomicInteger();
        map.forEach((key, stock) -> {
                skuStock.addAndGet(stock);
        });
        map.put(0L, skuStock.intValue());

        // 批量保存
        // <skukey, 库存>
        Map<String, String> keyMap = new HashMap<>();
        // 转换为key，value
        map.forEach((point, stock) -> {
            multiDecrementBO.setStockPointId(point);
            keyMap.put(getStockKey(multiDecrementBO), String.valueOf(stock));
        });
        stringRedisTemplate.opsForValue().multiSet(keyMap);
    }


    /**
     * 获取库存的key
     */
    private static String getStockKey(MultiDecrementBO multiDecrementBO) {
        String key = multiDecrementBO.getCachePrefix() + STOCK + multiDecrementBO.getActivityId() + StrUtil.C_UNDERLINE + multiDecrementBO.getSkuId();
        if (Objects.nonNull(multiDecrementBO.getStockPointId()) && multiDecrementBO.getStockPointId() > 0) {
            key =  key + StrUtil.C_UNDERLINE + multiDecrementBO.getStockPointId();
        }
        return key;
    }

    /**
     * 获取秒杀有多少库存
     */
    public Integer getSeckillStock(MultiDecrementBO multiDecrementBO) {
        LOG.info("扣减失败，聚合扣减进行，multiDecrementBO:{} ", multiDecrementBO);
        // 2.1 统计所有库存数量还剩多少
        int totalStock = 0;
        multiDecrementBO.setStockPointId(multiDecrementBO.getStockPointId() == null ? 0L : multiDecrementBO.getStockPointId());
        String stockStr = stringRedisTemplate.opsForValue().get(getStockKey(multiDecrementBO));
        int zoneStock;
        if (StrUtil.isNotBlank(stockStr)) {
            zoneStock = Integer.parseInt(stockStr);
        } else {
            zoneStock = 0;
        }
        totalStock += zoneStock;
        return totalStock;
    }

    /**
     * 获取秒杀有多少库存
     */
    public Map<String, Integer> listSeckillStock(List<MultiDecrementBO> multiDecrementList) {
        LOG.info("批量查询sku库存，multiDecrementList:{} ", multiDecrementList);
        List<String> keys = new ArrayList<>(multiDecrementList.size());
        List<String> skuPointKeys = new ArrayList<>(multiDecrementList.size());
        for (MultiDecrementBO multiDecrementBO : multiDecrementList) {
            if (Objects.isNull(multiDecrementBO.getStockPointId())) {
                multiDecrementBO.setStockPointId(0L);
            }
            keys.add(getStockKey(multiDecrementBO));
            skuPointKeys.add(multiDecrementBO.getSkuId() + Constant.UNDERLINE + multiDecrementBO.getStockPointId());
        }

        Map<String, Integer> stockMap = new HashMap<>(multiDecrementList.size());
        // 2.1 统计所有库存数量还剩多少
        // 遍历redis
        List<String> values = stringRedisTemplate.opsForValue().multiGet(keys);
        for (int j = 0; j < values.size(); j++) {
            String value = values.get(j);
            if (StrUtil.isBlank(value)) {
                continue;
            }
            int stock = Integer.parseInt(value);
            String key = skuPointKeys.get(j);
            if (stockMap.containsKey(key)) {
                stock = stockMap.get(key) + stock;
            }
            stockMap.put(key, stock);
        }
        return stockMap;
    }


    /**
     * 获取秒杀仓库有多少库存
     */
    public Map<Long, Integer> getSeckillStockPointMap(MultiDecrementBO multiDecrementBO) {
        Map<Long, Integer> pointMap = new HashMap<>();
        LOG.info("扣减失败，聚合扣减进行，multiDecrementBO:{} ", multiDecrementBO);
        // 2.1 统计所有库存数量还剩多少
        List<String> stockPointList = multiDecrementBO.getStockPointList();
        for (String pointId : stockPointList) {
            int totalStock = 0;
            multiDecrementBO.setStockPointId(Long.parseLong(pointId));
            int zoneStock = Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get(getStockKey(multiDecrementBO))));
            totalStock += zoneStock;
            pointMap.put(Long.parseLong(pointId), totalStock);
        }
        return pointMap;
    }

    /**
     * 扣减库存，返回扣减的分片id方便发mq的时候知道从哪里找到锁定的记录并且删除
     * <p>
     * 返回锁定记录所在的位置。因为redis有很多台，不知道这个锁定记录在哪里，所以要返回去，到时候生成订单成功的时候好把这条记录删掉
     */
    public void decrSeckillStocks(MultiDecrementBO multiDecrementBO) {

        // 扣减秒杀ksu库存，如果没有扣减成功会抛异常
        String result = reduceSeckillStockByZone(multiDecrementBO);
        // 超过购买上限
        if (MAX_RESULT.equals(result)) {
            // 本次秒杀商品限购
            String limitMsg = I18nMessage.getMessage("yami.seckill.limit.shop");
            String message = I18nMessage.getMessage("yami.seckill.num");
            throw new YamiShopBindException(limitMsg + multiDecrementBO.getMaxNum() + message);
        }
        // 库存不足，扣除失败
        if (NOT_ENOUGH.equals(result)) {
            // 库存不足
            throw new YamiShopBindException('[' + multiDecrementBO.getProdName() + ']' + I18nMessage.getMessage("yami.insufficient.inventory.area"));
        }
    }

    /**
     * 通过分片扣减秒杀商品库存并记录日志
     */
    public String reduceSeckillStockByZone(MultiDecrementBO multiDecrementBO) {
        String result = RedisLuaUtil.executeList(stringRedisTemplate,
                SECKILL_ORDER_STOCK_BYTES,
                Arrays.asList(multiDecrementBO.getCachePrefix(),
                        multiDecrementBO.getOrderNumber(),
                        multiDecrementBO.getActivityId().toString(),
                        multiDecrementBO.getProdCount().toString(),
                        multiDecrementBO.getUserId(),
                        multiDecrementBO.getMaxNum().toString(),
                        multiDecrementBO.getSkuId().toString(),
                        multiDecrementBO.getStockPointId().toString(),
                        String.valueOf(System.currentTimeMillis())
                )
        );
        return result;
    }

    /**
     * 取消订单，还原秒杀库存
     */
    public void rollbackSecKillStock(Long activityId, Long skuId, Integer count, String orderNumber, String userId, Long stockPointId) {
        // 幂等保证,还是写成lua比较好
        // 获取当前库存所使用的redis
        RedisLuaUtil.executeList(stringRedisTemplate,
                SECKILL_ORDER_CANCEL_STOCK_BYTES,
                Arrays.stream(StockUtil.loadQueryArray(MultiStockEnum.SECKILL.value(),
                        orderNumber,
                        activityId,
                        count,
                        userId,
                        skuId,
                        stockPointId
                )).toList()
        );
    }


    /**
     * 订单退款，还原秒杀库存
     */
    public void orderRefundRollbackSecKillStock(Long refundId, Long skuId, Long stockPointId, Integer count, long time) {
        // 获取当前库存所使用的redis
        RedisLuaUtil.executeList(stringRedisTemplate,
                SECKILL_ORDER_REFUND_RECOVERY_STOCK_BYTES,
                Arrays.asList(StockUtil.loadQueryArray(refundId, skuId, stockPointId, count, time))
        );
    }

    /**
     * 订单支付成功时，删除订单和订单锁定信息
     * @param count
     * @param orderNumber
     */
    public void handlePaySuccessSecKillStock(Integer count, String orderNumber) {
        RedisLuaUtil.executeList(stringRedisTemplate,
                SECKILL_ORDER_PAY_SUCCESS_STOCK_BYTES,
                Arrays.asList(MultiStockEnum.SECKILL.value(),
                        orderNumber));
    }

    public List<SkuStockVO> unLockFailOrderStock() {
        List<String> list = stringRedisTemplate.opsForList().range(CacheNames.ORDER_STOCK_LOCK, 0, 100);
        if (Objects.isNull(list)) {
            return Collections.emptyList();
        }
        long nowTime = System.currentTimeMillis();
        List<SkuStockVO> skuStockList = new ArrayList<>(list.size());
        for (String value : list) {
            String[] split = value.split(Constant.UNDERLINE);
            SkuStockVO skuStockVO = new SkuStockVO();
            long time = 0L;
            for (int i = 0; i < split.length; i++) {
                switch (i){
                    case 0 -> skuStockVO.setOrderNumber(split[i]);
                    case 1 -> skuStockVO.setStockPointId(Long.parseLong(split[i]));
                    case 2 -> skuStockVO.setSkuId(Long.parseLong(split[i]));
                    case 3 -> time = Long.parseLong(split[i]);
                    default -> {}
                }
            }
            if (time == 0 || time > nowTime) {
                // 未达到处理时间的订单，先不处理
                break;
            }
            skuStockList.add(skuStockVO);
        }
        return skuStockList;
    }


    public List<MultiDecrementBO> unLockFailSeckillOrderStock(String key) {
        List<String> list = stringRedisTemplate.opsForList().range(key, 0, 100);
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        long nowTime = System.currentTimeMillis();
        List<MultiDecrementBO> multiDecrementList = new ArrayList<>(list.size());
        for (String value : list) {
            long time = 0L;
            String[] split = value.split(Constant.UNDERLINE);
            MultiDecrementBO multiDecrementBO = new MultiDecrementBO();
            for (int i = 0; i < split.length; i++) {
                switch (i){
                    case 0 -> multiDecrementBO.setOrderNumber(split[i]);
                    case 1 -> multiDecrementBO.setStockPointId(Long.parseLong(split[i]));
                    case 2 -> multiDecrementBO.setSkuId(Long.parseLong(split[i]));
                    case 3 -> time = Long.parseLong(split[i]);
                    case 4 -> multiDecrementBO.setActivityId(Long.parseLong(split[i]));
                    case 5 -> multiDecrementBO.setProdCount(Integer.parseInt(split[i]));
                    case 6 -> multiDecrementBO.setUserId(split[i]);
                    default -> {}
                }
            }
            if (time == 0 || time > nowTime) {
                // 未达到处理时间的订单，先不处理
                break;
            }
            multiDecrementList.add(multiDecrementBO);
        }
        return multiDecrementList;
    }


    public void trimList(String key, Integer size) {
        stringRedisTemplate.opsForList().trim(key, size, size -1);
    }


    public void batchPushList(String key, Collection<String> recoveryList) {
        if (CollUtil.isEmpty(recoveryList)) {
            return;
        }
        stringRedisTemplate.opsForList().rightPushAll(key, recoveryList);
    }

    public String getByKey(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public long countListByKey(String keyName) {
        Long size = stringRedisTemplate.opsForList().size(keyName);
        if (Objects.nonNull(size)) {
            return size;
        }
        return 0;
    }

    public List<String> batchListByKey(String keyName, int endIndex) {
        List<String> list = stringRedisTemplate.opsForList().range(keyName, 0, endIndex);
        if (Objects.isNull(list)) {
            return Collections.emptyList();
        }
        return list;
    }
}
